package services;

import domain.entities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import services.exceptions.ExistentInvestigadorProtocoloCentroException;
import services.exceptions.InexistentProtocoloCentroException;

import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: scamisay
 * Date: 9/10/12
 * Time: 1:57 AM
 * To change this template use File | Settings | File Templates.
 */
@Service
public class InvestigadorProtocoloCentroService {

    private InvestigadorProtocoloCentroDAO investigadorProtocoloCentroDAO;
    private ProtocoloCentroDAO protocoloCentroDAO;
    private CenterCRUDService centerCRUDService;
    private InvolucradoDAO involucradoDAO;
    private EthicsCommitteeCRUDService ethicsCommitteeCRUDService;
    private RoleNameProtocoloCompromisoPaisDAO roleNameProtocoloCompromisoPaisDAO;
    private RoleDAO roleDAO;

    @Autowired
    public InvestigadorProtocoloCentroService(InvestigadorProtocoloCentroDAO investigadorProtocoloCentroDAO,
                                              ProtocoloCentroDAO protocoloCentroDAO,
                                              CenterCRUDService centerCRUDService,
                                              InvolucradoDAO involucradoDAO,
                                              EthicsCommitteeCRUDService ethicsCommitteeCRUDService, RoleNameProtocoloCompromisoPaisDAO roleNameProtocoloCompromisoPaisDAO, RoleDAO roleDAO) {
        this.investigadorProtocoloCentroDAO = investigadorProtocoloCentroDAO;
        this.protocoloCentroDAO = protocoloCentroDAO;
        this.centerCRUDService = centerCRUDService;
        this.involucradoDAO = involucradoDAO;
        this.ethicsCommitteeCRUDService = ethicsCommitteeCRUDService;
        this.roleNameProtocoloCompromisoPaisDAO = roleNameProtocoloCompromisoPaisDAO;
        this.roleDAO = roleDAO;
    }

    public InvestigadorProtocoloCentroService() {
    }

    public List<InvestigadorProtocoloCentro> findAllBy(ProtocoloCompromisopais protocoloCompromisopais){
//        List<InvestigadorProtocoloCentro> list = investigadorProtocoloCentroDAO.findAll();
//        List<ProtocoloCentro> pcentros = findAllProtocoloCentroByProtocoloCompromisopais(protocoloCompromisopais);
//
//        List<InvestigadorProtocoloCentro> result = new ArrayList<InvestigadorProtocoloCentro>();
//
//
//        for(InvestigadorProtocoloCentro ipc : list){
//            for(ProtocoloCentro pc : pcentros){
//                ProtocoloCentroId aux = pc.getId();
//                if(aux.equals(ipc.getProtocoloCentro().getId())){
//                    result.add(ipc);
//                    break;
//                }
//            }
//        }
//
//        return result;
        List<InvestigadorProtocoloCentro> list = investigadorProtocoloCentroDAO.findAll();
        List<InvestigadorProtocoloCentro> result = new ArrayList<InvestigadorProtocoloCentro>();
        for( InvestigadorProtocoloCentro ipc : list){
            if(ipc.getProtocoloCentro().getProtocoloCompromisopais().equals(protocoloCompromisopais)){
                result.add(ipc);
            }
        }
        return result;
    }

    private List<ProtocoloCentro> findAllProtocoloCentroByProtocoloCompromisopais(ProtocoloCompromisopais protocoloCompromisopais){
        List<ProtocoloCentro> list = protocoloCentroDAO.findAll();
        Iterator<ProtocoloCentro> it = list.iterator();
        while(it.hasNext()){
            ProtocoloCompromisopais pIt = it.next().getProtocoloCompromisopais();
            ProtocoloCompromisopaisId idIt = pIt.getId();
            if(!idIt.getProtocoloid().equals(protocoloCompromisopais.getId().getProtocoloid())
                    ||
                    !idIt.getCompromisopaisid().equals(protocoloCompromisopais.getId().getCompromisopaisid())
                    ){
                it.remove();
            }
        }
        return list;
    }

    //TODO: remove
//    public void save(InvestigadorProtocoloCentro investigadorProtocoloCentro){
//        investigadorProtocoloCentroDAO.attachDirty(investigadorProtocoloCentro);
//    }

    public void save(InvestigadorProtocoloCentro investigadorProtocoloCentro,
                     ProtocoloCompromisopais protocoloCompromisopais)
            throws InexistentProtocoloCentroException, ExistentInvestigadorProtocoloCentroException {

        if(investigadorProtocoloCentro.isNew()){
            if(existsIPC(investigadorProtocoloCentro,protocoloCompromisopais)){
                throw new ExistentInvestigadorProtocoloCentroException();
            }
           _createIPC(investigadorProtocoloCentro,protocoloCompromisopais);
        }else{
            /*edit*/
            _createIPC(investigadorProtocoloCentro,protocoloCompromisopais);
        }

        /*replico tres campos de PI para todos los IPC con combinacion (Site,PI) */
        List<InvestigadorProtocoloCentro> ipcs = investigadorProtocoloCentroDAO.findAll();
        Centro centro = investigadorProtocoloCentro.getProtocoloCentro().getCentro();
        Involucrado pi = investigadorProtocoloCentro.getPrincipalInvestigator();
        for(InvestigadorProtocoloCentro ipc : ipcs){
            if( ipc.getProtocoloCentro().getCentro().equals(centro)
                    &&
                ipc.getPrincipalInvestigator().equals(pi)){
                ipc.setPiPosition(investigadorProtocoloCentro.getPiPosition());
                ipc.setPiLocation(investigadorProtocoloCentro.getPiLocation());
                ipc.setPiPhoneNumber(investigadorProtocoloCentro.getPiPhoneNumber());
                investigadorProtocoloCentroDAO.attachDirty(ipc);
            }
        }
    }

    public InvestigadorProtocoloCentro getIPCDataForPIAndSite(Integer investigatorId, Integer siteId){
        List<InvestigadorProtocoloCentro> ipcs = investigadorProtocoloCentroDAO.findAll();
        Centro centro = centerCRUDService.findById(siteId);
        Involucrado pi = involucradoDAO.findById(investigatorId);
        for(InvestigadorProtocoloCentro ipc : ipcs){
            if( ipc.getProtocoloCentro().getCentro().equals(centro)
                    &&
                    ipc.getPrincipalInvestigator().equals(pi)){
                return ipc;
            }
        }
        return new InvestigadorProtocoloCentro();
    }

    public void save2(InvestigadorProtocoloCentro investigadorProtocoloCentro,
                     ProtocoloCompromisopais protocoloCompromisopais,
                     boolean selected,
                     Collection<ComiteEtica> comites){

        /*Preparo los comites de ética*/
        Set<ComiteEtica> comitesInput = new HashSet<ComiteEtica>();
        if(comites.size()>0){
            comitesInput =  cargarComitesEtica(comites);
        }

        if(selected){
            investigadorProtocoloCentro.setStatus(SiteState.Selected);
        }else{
            investigadorProtocoloCentro.setStatus(SiteState.Postulated);
        }

        if(investigadorProtocoloCentro.getId().getInvestigadorid() == null){
            /*new*/
            if(!selected){
                investigadorProtocoloCentro.setStatus(SiteState.Postulated);
            }
            create(investigadorProtocoloCentro,protocoloCompromisopais,comitesInput);
        }else{
            /*edit*/
            ProtocoloCentro protocoloCentro = findByInvestigadorProcoloCentro(investigadorProtocoloCentro,protocoloCompromisopais);
            //delete(investigadorProtocoloCentro);
            update(investigadorProtocoloCentro, protocoloCompromisopais, protocoloCentro,comitesInput);
        }
    }

    private  Set<ComiteEtica> cargarComitesEtica(Collection<ComiteEtica> comites) {
        Set<ComiteEtica> comitesConvertidos = new HashSet<ComiteEtica>();
        Iterator<ComiteEtica> it = comites.iterator();
        while( it.hasNext() ){
            ComiteEtica actual = it.next();
            if(actual != null && actual.getId() != null){
                int id = actual.getId();

                ComiteEtica ce = ethicsCommitteeCRUDService.findById(id);
                comitesConvertidos.add(ce);
            }
        }
       return comitesConvertidos;
    }

    private void update(InvestigadorProtocoloCentro investigadorProtocoloCentro,
                        ProtocoloCompromisopais protocoloCompromisopais,
                        ProtocoloCentro protocoloCentro,
                        Set<ComiteEtica> comites) {
        _create(investigadorProtocoloCentro,
                protocoloCompromisopais,
                protocoloCentro,
                comites
        );
    }

    private ProtocoloCentro findByInvestigadorProcoloCentro(InvestigadorProtocoloCentro ipc, ProtocoloCompromisopais protocoloCompromisopais){
        ProtocoloCentroId pciid = new ProtocoloCentroId();

        Integer centroId = ipc.getProtocoloCentro().getCentro().getId();

        pciid.setCentroid(centroId);
        pciid.setProtocoloCompromisopaiscompromisopaisid(
                protocoloCompromisopais.getId().getCompromisopaisid()
        );
        pciid.setProtocoloCompromisopaisprotocoloid(
                protocoloCompromisopais.getId().getProtocoloid()
        );

        return protocoloCentroDAO.findById(pciid);
    }

    private boolean existsIPC(InvestigadorProtocoloCentro investigadorProtocoloCentro,
                              ProtocoloCompromisopais protocoloCompromisopais){
        Integer centroId = investigadorProtocoloCentro.getProtocoloCentro().getCentro().getId();
        Centro centro = centerCRUDService.findById(centroId);


        Integer involucradoId = investigadorProtocoloCentro.getPrincipalInvestigator().getId();
        Involucrado pi = involucradoDAO.findById(involucradoId);


        InvestigadorProtocoloCentroId id = new InvestigadorProtocoloCentroId();
        id.setInvestigadorid(pi.getId());
        id.setProtocoloCentrocentroid(centro.getId());
        id.setProtocoloCentroprotocoloCompromisopaiscompromisopaisid(
                protocoloCompromisopais.getId().getCompromisopaisid()
        );
        id.setProtocoloCentroprotocoloCompromisopaisprotocoloid(
                protocoloCompromisopais.getId().getProtocoloid()
        );

        InvestigadorProtocoloCentro persisted = investigadorProtocoloCentroDAO.findById(id);
        return persisted != null;
    }

    /*
   * Creo un InvestigadorProtocoloCentro en funcion de los datos recibidos
   * */
    private void _createIPC(InvestigadorProtocoloCentro investigadorProtocoloCentro,
                         ProtocoloCompromisopais protocoloCompromisopais)
                    throws InexistentProtocoloCentroException{
        //Recupero el centro
        Integer centroId = investigadorProtocoloCentro.getProtocoloCentro().getCentro().getId();
        Centro centro = centerCRUDService.findById(centroId);

        /*Asigno ProtocoloCentro*/
        ProtocoloCentroId pid =  new ProtocoloCentroId(centroId, protocoloCompromisopais.getProtocolo().getId(), protocoloCompromisopais.getCompromisopais().getId());
        ProtocoloCentro p = protocoloCentroDAO.findById(pid);

        /*Valido si existe ProtocoloCentro*/
        if(p==null){
            throw new InexistentProtocoloCentroException();
        }else{
            /*Set values inserted in the screen*/
            Integer icMemberId = investigadorProtocoloCentro.getProtocoloCentro().getECMember().getId();
            Involucrado ecMember = icMemberId != null ? involucradoDAO.findById(icMemberId): null;
            p.setECMember(ecMember);
        }

        //armo pi
        Integer involucradoId = investigadorProtocoloCentro.getPrincipalInvestigator().getId();
        Involucrado pi = involucradoDAO.findById(involucradoId);

        Integer involucradoSIId = investigadorProtocoloCentro.getSubInvestigator().getId();
        if(involucradoSIId != null){
            Involucrado si = involucradoDAO.findById(involucradoSIId);
            investigadorProtocoloCentro.setSubInvestigator(si);
        }else{
            investigadorProtocoloCentro.setSubInvestigator(null);
        }


        InvestigadorProtocoloCentroId id = new InvestigadorProtocoloCentroId();
        id.setInvestigadorid(pi.getId());
        id.setProtocoloCentrocentroid(centro.getId());
        id.setProtocoloCentroprotocoloCompromisopaiscompromisopaisid(
                protocoloCompromisopais.getId().getCompromisopaisid()
        );
        id.setProtocoloCentroprotocoloCompromisopaisprotocoloid(
                protocoloCompromisopais.getId().getProtocoloid()
        );

        //asigno valores a investigadorProtocoloCentro
        investigadorProtocoloCentro.setProtocoloCentro(p);
        investigadorProtocoloCentro.setPrincipalInvestigator(pi);
        investigadorProtocoloCentro.setId(id);

        Iterator<RoleNameProtocoloCompromisoPais> iterator = investigadorProtocoloCentro.getSiteStaff().iterator();
        while (iterator.hasNext()){
            RoleNameProtocoloCompromisoPais r = iterator.next();
            if(r == null || r.getRole() == null || r.getRole().getId() == null ||
                    r.getInvolucrado() == null || r.getInvolucrado().getId() == null)
                iterator.remove();
            else if(r.getRole().getId()==0){
                r.setInvolucrado(involucradoDAO.findById(r.getInvolucrado().getId()));
                r.setRole(null);
            }else{
                r.setInvolucrado(involucradoDAO.findById(r.getInvolucrado().getId()));
                r.setRole(roleDAO.findById(r.getRole().getId()));
                r.setOtherRole("");
            }
        }

        //guardo
        investigadorProtocoloCentroDAO.merge(investigadorProtocoloCentro);


    }

    /*
   * Creo un InvestigadorProtocoloCentro en funcion de los datos recibidos
   * */
    private void _create(InvestigadorProtocoloCentro investigadorProtocoloCentro,
                         ProtocoloCompromisopais protocoloCompromisopais,
                         ProtocoloCentro protocoloCentro,
                         Set<ComiteEtica> comites) {
        //Recupero el centro
        Integer centroId = investigadorProtocoloCentro.getProtocoloCentro().getCentro().getId();
        Centro centro = centerCRUDService.findById(centroId);

        /*Si no hay ProtocoloCentro asignado lo creo*/
        ProtocoloCentroId pid =  new ProtocoloCentroId(centroId, protocoloCompromisopais.getProtocolo().getId(), protocoloCompromisopais.getCompromisopais().getId());
        ProtocoloCentro p = protocoloCentroDAO.findById(pid);
        if(protocoloCentro == null){
            if(p == null)
                protocoloCentro = createProtocoloCentro(protocoloCompromisopais, centro,comites);
            else
                protocoloCentro = p;
        }else{
//            protocoloCentro.setComiteEticas(comites);
            protocoloCentro.setCcis("lalala");
//            _setComitesToProtocolo(protocoloCentro,comites);
        }


        //TODO: agregar siteStaff de acuerdo al l&f
//        List<RoleNameProtocoloCompromisoPais> siteStaff = new ArrayList<RoleNameProtocoloCompromisoPais>(0);
//        investigadorProtocoloCentro.setSiteStaff(siteStaff);

        //armo pi
        Integer involucradoId = investigadorProtocoloCentro.getPrincipalInvestigator().getId();
        Involucrado pi = involucradoDAO.findById(involucradoId);

        Integer involucradoSIId = investigadorProtocoloCentro.getSubInvestigator().getId();
        Involucrado si = involucradoDAO.findById(involucradoSIId);


        InvestigadorProtocoloCentroId id = new InvestigadorProtocoloCentroId();
        id.setInvestigadorid(pi.getId());
        id.setProtocoloCentrocentroid(centro.getId());
        id.setProtocoloCentroprotocoloCompromisopaiscompromisopaisid(
                protocoloCompromisopais.getId().getCompromisopaisid()
        );
        id.setProtocoloCentroprotocoloCompromisopaisprotocoloid(
                protocoloCompromisopais.getId().getProtocoloid()
        );

        //asigno valores a investigadorProtocoloCentro
        investigadorProtocoloCentro.setProtocoloCentro(protocoloCentro);
        investigadorProtocoloCentro.setPrincipalInvestigator(pi);
        investigadorProtocoloCentro.setSubInvestigator(si);
        investigadorProtocoloCentro.setId(id);

        Iterator<RoleNameProtocoloCompromisoPais> iterator = investigadorProtocoloCentro.getSiteStaff().iterator();
        while (iterator.hasNext()){
            RoleNameProtocoloCompromisoPais r = iterator.next();
            if(r == null || r.getRole() == null || r.getRole().getId() == null || r.getInvolucrado() == null || r.getInvolucrado().getId() == null)
                iterator.remove();
            else{
                r.setInvolucrado(involucradoDAO.findById(r.getInvolucrado().getId()));
                r.setRole(roleDAO.findById(r.getRole().getId()));
            }
        }

        //guardo
        investigadorProtocoloCentroDAO.merge(investigadorProtocoloCentro);
    }

    private void _setComitesToProtocolo(ProtocoloCentro protocoloCentro, Set<ComiteEtica> comites) {

        /*Elimino de los comites de eticias ya existentes el protocoloCentro*/
//        Set<ComiteEtica> comiteVinculados = protocoloCentro.getComiteEticas();
//        for(ComiteEtica ce : comiteVinculados){
//            Set<ProtocoloCentro> protocolos = ce.getProtocoloCentros();
//            Set<ProtocoloCentro> protocoloASacar = new HashSet<ProtocoloCentro>();
//            protocoloASacar.add(protocoloCentro);
//            protocolos.retainAll(protocoloASacar);
//            ce.setProtocoloCentros(protocolos);
//            ethicsCommitteeCRUDService.save(ce);
//        }

        /*Guardo en los comites que traigo el ptrotocoloCentro*/
//        for(ComiteEtica ce : comites){
//            Set<ProtocoloCentro> protocolos = ce.getProtocoloCentros();
//            protocolos.add(protocoloCentro);
//            ce.setProtocoloCentros(protocolos);
//            ethicsCommitteeCRUDService.save(ce);
//        }

    }

    private ProtocoloCentro createProtocoloCentro(ProtocoloCompromisopais protocoloCompromisopais,
                                                  Centro centro,
                                                  Set<ComiteEtica> comites){
        //Armo protocolo centro
        ProtocoloCentro protocoloCentro = new ProtocoloCentro();
        protocoloCentro.setCentro(centro);
        protocoloCentro.setProtocoloCompromisopais(protocoloCompromisopais);

        ProtocoloCentroId protocoloCentroId = new ProtocoloCentroId();
        protocoloCentroId.setCentroid(centro.getId());
        protocoloCentroId.setProtocoloCompromisopaiscompromisopaisid(
                protocoloCompromisopais.getId().getCompromisopaisid()
        );
        protocoloCentroId.setProtocoloCompromisopaisprotocoloid(
                protocoloCompromisopais.getId().getProtocoloid()
        );

//        protocoloCentro.setId(protocoloCentroId);
        protocoloCentro.setProtocoloCentroprotocoloid(
                protocoloCompromisopais.getId().getProtocoloid()
        );
        protocoloCentro.setProtocoloCentrocentroid(
                protocoloCompromisopais.getId().getCompromisopaisid()
        );
//        protocoloCentro.setComiteEticas(comites);

        /*guardar protocolo centro*/
        protocoloCentroDAO.attachDirty(protocoloCentro);

        return protocoloCentro;
    }

    private void delete(InvestigadorProtocoloCentro investigadorProtocoloCentro) {
        InvestigadorProtocoloCentro investigadorProtocoloCentroAux = investigadorProtocoloCentroDAO.findById(
                investigadorProtocoloCentro.getId()
        );
        investigadorProtocoloCentroDAO.delete(investigadorProtocoloCentroAux);
    }

    private void create(InvestigadorProtocoloCentro investigadorProtocoloCentro,
                        ProtocoloCompromisopais protocoloCompromisopais,
                        Set<ComiteEtica> comites) {
        _create(investigadorProtocoloCentro,
                protocoloCompromisopais,
                null,
                comites
        );
    }

    private void createIPC(InvestigadorProtocoloCentro investigadorProtocoloCentro,
                        ProtocoloCompromisopais protocoloCompromisopais) {
//        _create(investigadorProtocoloCentro,
//                protocoloCompromisopais,
//                null,
//                comites
//        );
    }

    public InvestigadorProtocoloCentro findById(InvestigadorProtocoloCentroId id){
        return investigadorProtocoloCentroDAO.findById(id);
    }

    public Set<Involucrado> findByRolesProtocolosCompromisopaises(List<Role> roles, List<Compromisopais> cps, List<Protocolo> protocolos){
        List<InvestigadorProtocoloCentro> ipc = investigadorProtocoloCentroDAO.findAllByProtocolosCompromisopaises(cps, protocolos);
        Iterator<InvestigadorProtocoloCentro> it = ipc.iterator();
        Set<Involucrado> involucrados = new HashSet<Involucrado>();

        Role ppalInv = new Role(Role.ID_PRINCIPAL_INVESTIGATOR,Role.PRINCIPAL_INVESTIGATOR);
        Role subInv = new Role(Role.ID_SUB_INVESTIGATOR,Role.SUB_INVESTIGATOR);

        while(it.hasNext()){
            InvestigadorProtocoloCentro actual = it.next();
            if(actual.getOngoing() != null && actual.getOngoing().getInitialPackageSent() != null){
                if (roles.contains(ppalInv))
                    involucrados.add(actual.getPrincipalInvestigator());
                if (roles.contains(subInv) && actual.isSubRequiresSafety())
                    involucrados.add(actual.getSubInvestigator());
                Iterator<RoleNameProtocoloCompromisoPais> itRn = actual.getSiteStaff().iterator();
                while(itRn.hasNext()){
                    RoleNameProtocoloCompromisoPais rnActual = itRn.next();
                    if(rnActual.isSafety() && roles.contains(rnActual.getRole()))
                        involucrados.add(rnActual.getInvolucrado());
                }
            }
        }
        return involucrados;
    }


//TODO: gotta see this!!!       private List<InvestigadorProtocoloCentro> findByInvestigator(Involucrado involucrado){
//            return investigadorProtocoloCentroDAO.findByInvestigator(involucrado);
//        }}

    public ArrayList<Involucrado> findPrincipalInvestigatorsByProtocol(ProtocoloCompromisopais pcp) {
        ArrayList<InvestigadorProtocoloCentro> investigators = (ArrayList<InvestigadorProtocoloCentro>) investigadorProtocoloCentroDAO.findAllByProtocoloCompromisopais(pcp);
        ArrayList<Involucrado> principalInvestigators = new ArrayList<Involucrado>(0);

        for(InvestigadorProtocoloCentro i : investigators){
            principalInvestigators.add(i.getPrincipalInvestigator());
        }
        return principalInvestigators;
    }

    public InvestigadorProtocoloCentro findByInvestigatorAndProtocoloCompromisoPais(ProtocoloCompromisopais protocoloCompromisopais, Integer investigatorId) {
        ArrayList<InvestigadorProtocoloCentro> ipcs = (ArrayList<InvestigadorProtocoloCentro>) investigadorProtocoloCentroDAO.findAllByProtocoloCompromisopaisAndInvestigator(protocoloCompromisopais, investigatorId);
        if(ipcs.isEmpty())
            return null;
        return ipcs.get(0);
    }

    public void merge(InvestigadorProtocoloCentro investigadorProtocoloCentro){
        investigadorProtocoloCentroDAO.merge(investigadorProtocoloCentro);
        return;
    }

//    public List<Involucrado> findExistentStaff(ProtocoloCentro protocoloCentro) {
//        for(InvestigadorProtocoloCentro ipc : ){
//
//        }
//    }
}
